//***************************************************************************
//   Copyright(c)2020, Xidian University D405.
//           All rights reserved
//
//   File name       :   axi_write_channel_rxfifo.v
//   Module name     :   axi_write_channel_rxfifo
//   Author          :   Xiong Yimeng
//   Date            :   2022/03/18
//   Version         :   v1.00
//   Edited by       :   Xiong Yimeng
//***************************************************************************
// async axis fifo

// DATA INFO
// width 64 -> 128
// depth 10

// LENGTH INFO
// width 8
// depth 10

// AXIS INFO
// axis tstrb disable
// axis tid disable
// axis tdest disable
//`undef VCS_MODEL
module axi_write_channel_rxfifo_40g #(
        parameter MAC_DWIDTH = 64  , // MAC Data Width
        parameter MAC_KWIDTH = 8   , // MAC Keep Width
        parameter MAC_KBITS  = 3   , // MAC Keep Bits
        parameter MAC_UWIDTH = 4   , // MAC User Width
        parameter DMA_DWIDTH = 128 , // DMA Data Width
        parameter MEM_AWIDTH = 10  , // MEM Addr Width
        parameter LEN_DWIDTH = 11  , // LEN Data Width
        parameter LEN_AWIDTH = 10    // LEN Addr Width
    )(
        // SYS
        input  wclk  , // 625MHz MAC
        input  rclk  , // 100MHz DMA
        input  rstn_wclk,
        input  rstn_rclk,
        //input  arstn , // Low Active, Async to Both Sides

        // WCLK
        input                     s_axis_tvalid_i ,
        input                     s_axis_tlast_i  ,
        input  [MAC_KWIDTH-1 : 0] s_axis_tkeep_i  ,
        input  [MAC_DWIDTH-1 : 0] s_axis_tdata_i  ,
        output                    s_axis_tready_o ,
        input  [MAC_UWIDTH-1 : 0] s_axis_tuser_i  ,

        // RCLK
        input                     rd_leng_en_i ,
        input                     rd_data_en_i ,
        output [LEN_DWIDTH-1 : 0] rd_leng_o    ,
        output [DMA_DWIDTH-1 : 0] rd_dout_o    ,
        output                    empty_o      ,
        output                    data_empty_o ,
        input [11:0]        ram_dp_cfg_register,
        input               mbist_test
    );
/*
//======================================================================================================
// RSTN CDC
reg rstn_wclk_sync0 ;
reg rstn_wclk_sync1 ;
reg rstn_rclk_sync0 ;
reg rstn_rclk_sync1 ;

always @( posedge wclk or negedge arstn ) begin
    if ( !arstn ) begin
        rstn_wclk_sync0 <= 1'b0 ;
        rstn_wclk_sync1 <= 1'b0 ;
    end
    else begin
        rstn_wclk_sync0 <= 1'b1 ;
        rstn_wclk_sync1 <= rstn_wclk_sync0 ;
    end
end

always @( posedge rclk or negedge arstn ) begin
    if ( !arstn ) begin
        rstn_rclk_sync0 <= 1'b0 ;
        rstn_rclk_sync1 <= 1'b0 ;
    end
    else begin
        rstn_rclk_sync0 <= 1'b1 ;
        rstn_rclk_sync1 <= rstn_rclk_sync0 ;
    end
end
*/
//======================================================================================================
// IN Sample
reg                    s_axis_tvalid_ff ;
reg                    s_axis_tlast_ff  ;
reg [MAC_KWIDTH-1 : 0] s_axis_tkeep_ff  ;
reg [MAC_DWIDTH-1 : 0] s_axis_tdata_ff  ;

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        s_axis_tvalid_ff <= 1'b0 ;
        s_axis_tlast_ff  <= 1'b0 ;
        s_axis_tkeep_ff  <= 'd0  ;
        s_axis_tdata_ff  <= 'd0  ;
    end
    else if ( s_axis_tready_o ) begin
        s_axis_tvalid_ff <= s_axis_tvalid_i ;
        s_axis_tlast_ff  <= s_axis_tlast_i  ;
        s_axis_tkeep_ff  <= s_axis_tkeep_i  ;
        s_axis_tdata_ff  <= s_axis_tdata_i  ;
    end
    else begin
        s_axis_tvalid_ff <= s_axis_tvalid_ff ;
        s_axis_tlast_ff  <= s_axis_tlast_ff  ;
        s_axis_tkeep_ff  <= s_axis_tkeep_ff  ;
        s_axis_tdata_ff  <= s_axis_tdata_ff  ;
    end
end

//======================================================================================================
// LEN Input
wire                    wr_en    ;
reg                     last_d1  ; // 1T after s_axis_tlast_ff
reg  [LEN_DWIDTH-1 : 0] len_cnt  ;
reg  [   MAC_KBITS : 0] keep_cnt ; // comb, worse timing

assign wr_en = s_axis_tvalid_ff & s_axis_tready_o ;

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        last_d1 <= 1'b0 ;
    end
    else if ( wr_en ) begin
        last_d1 <= s_axis_tlast_ff ;
    end
    else begin
        last_d1 <= 1'b0 ;
    end
end

always @(*) begin
    case ( s_axis_tkeep_ff )
        8'b00000001: keep_cnt = 'd1 ;
        8'b00000011: keep_cnt = 'd2 ;
        8'b00000111: keep_cnt = 'd3 ;
        8'b00001111: keep_cnt = 'd4 ;
        8'b00011111: keep_cnt = 'd5 ;
        8'b00111111: keep_cnt = 'd6 ;
        8'b01111111: keep_cnt = 'd7 ;
        8'b11111111: keep_cnt = 'd8 ;
        /*
        8'b10000000: keep_cnt = 'd1 ;
        8'b11000000: keep_cnt = 'd2 ;
        8'b11100000: keep_cnt = 'd3 ;
        8'b11110000: keep_cnt = 'd4 ;
        8'b11111000: keep_cnt = 'd5 ;
        8'b11111100: keep_cnt = 'd6 ;
        8'b11111110: keep_cnt = 'd7 ;
        8'b11111111: keep_cnt = 'd8 ;
        */
            default: keep_cnt = 'd0 ;
    endcase
end

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        len_cnt <= 'd0 ;
    end
    else begin
        case ( {wr_en, last_d1} )
                2'b00: len_cnt <= len_cnt ;
                2'b01: len_cnt <= 'd0 ;
                2'b10: len_cnt <= len_cnt + keep_cnt ;
                2'b11: len_cnt <= keep_cnt ;
        endcase
    end
end

//======================================================================================================
// DATA Input
reg                    even_flag ; // 1T after wr_en
reg                    wr_dval   ;
reg [DMA_DWIDTH-1 : 0] wr_din    ;
reg [MAC_DWIDTH-1 : 0] data_d1   ;
reg [MAC_DWIDTH-1 : 0] keep_mask ; // comb, worse timing

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        even_flag <= 1'b0 ;
    end
    else begin
        case ( {wr_en, s_axis_tlast_ff} )
                2'b10: even_flag <= ~even_flag ;
                2'b11: even_flag <= 1'b0 ;
              default: even_flag <= even_flag ;
        endcase
    end
end

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        data_d1 <= 'd0 ;
    end
    else if ( wr_en ) begin
        data_d1 <= s_axis_tdata_ff ;
    end
    else begin
        data_d1 <= data_d1 ;
    end
end

always @(*) begin
    case ( s_axis_tkeep_ff )
        8'b11111111: keep_mask = 64'hffffffffffffffff ;
        8'b01111111: keep_mask = 64'h00ffffffffffffff ;
        8'b00111111: keep_mask = 64'h0000ffffffffffff ;
        8'b00011111: keep_mask = 64'h000000ffffffffff ;
        8'b00001111: keep_mask = 64'h00000000ffffffff ;
        8'b00000111: keep_mask = 64'h0000000000ffffff ;
        8'b00000011: keep_mask = 64'h000000000000ffff ;
        8'b00000001: keep_mask = 64'h00000000000000ff ;
        /*                       
        8'b10000000: keep_mask = 64'hff00000000000000 ;
        8'b11000000: keep_mask = 64'hffff000000000000 ;
        8'b11100000: keep_mask = 64'hffffff0000000000 ;
        8'b11110000: keep_mask = 64'hffffffff00000000 ;
        8'b11111000: keep_mask = 64'hffffffffff000000 ;
        8'b11111100: keep_mask = 64'hffffffffffff0000 ;
        8'b11111110: keep_mask = 64'hffffffffffffff00 ;
        8'b11111111: keep_mask = 64'hffffffffffffffff ;
        */
            default: keep_mask = 'd0                ;
    endcase
end

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        wr_din  <= 'd0 ;
        wr_dval <= 1'b0 ;
    end
    else begin
        casez ( {wr_en, even_flag, s_axis_tlast_ff} )
                3'b11?: begin
                    wr_din  <= { s_axis_tdata_ff & keep_mask , data_d1 } ;
//                    wr_din  <= { data_d1 , s_axis_tdata_ff & keep_mask } ;
                    wr_dval <= 1'b1 ;
                end
                3'b101: begin
                    wr_din  <= { 64'd0 , s_axis_tdata_ff & keep_mask } ;
//                    wr_din  <= { s_axis_tdata_ff & keep_mask , 64'd0 } ;
                    wr_dval <= 1'b1 ;
                end
               default: begin
                    wr_din  <= wr_din ;
                    wr_dval <= 1'b0 ;
                end
        endcase
    end
end

//======================================================================================================
// Output
wire full_o ;
assign s_axis_tready_o = !full_o ;

//======================================================================================================
// Instance
`ifdef VCS_MODEL
    native_fifo_2T #( .DW(LEN_DWIDTH), .AW(LEN_AWIDTH) ) len_fifo(
        .wclk          ( wclk      ),
        .wr_rstn       ( rstn_wclk ),
        .rclk          ( rclk      ),
        .rd_rstn       ( rstn_rclk ),

        .wr_en_i       ( last_d1 ), // 1T after s_axis_tlast_i
        .wr_din_i      ( len_cnt ), // 1T after wr_en
        .full_o        (         ),
        .rd_en_i       ( rd_leng_en_i ),
        .rd_dout_o     ( rd_leng_o    ),
        .empty_o       ( empty_o      )
    );

    native_fifo_2T #( .DW(DMA_DWIDTH), .AW(MEM_AWIDTH) ) data_fifo(
        .wclk          ( wclk      ),
        .wr_rstn       ( rstn_wclk ),
        .rclk          ( rclk      ),
        .rd_rstn       ( rstn_rclk ),

        .wr_en_i       ( wr_dval ), // 1T after wr_en
        .wr_din_i      ( wr_din  ), // 1T after wr_en
        .full_o        ( full_o  ),
        
        .rd_en_i       ( rd_data_en_i ),
        .rd_dout_o     ( rd_dout_o    ),
        .empty_o       ( data_empty_o )
    );
`else
    async_fifo_d4096_w11 len_fifo_rx_40g(
        .wclk          ( wclk      ),
        .wr_rstn       ( rstn_wclk ),
        .rclk          ( rclk      ),
        .rd_rstn       ( rstn_rclk ),

        .wr_en_i       ( last_d1 ), // 1T after s_axis_tlast_i
        .wr_din_i      ( len_cnt ), // 1T after wr_en
        .full_o        (         ),
        .rd_en_i       ( rd_leng_en_i ),
        .rd_dout_o     ( rd_leng_o    ),
        .empty_o       ( empty_o      ),
        .ram_dp_cfg_register        (ram_dp_cfg_register)
    );

    async_fifo_d16384_w128 data_fifo_rx_40g(
        .wclk          ( wclk      ),
        .wr_rstn       ( rstn_wclk ),
        .rclk          ( rclk      ),
        .rd_rstn       ( rstn_rclk ),

        .wr_en_i       ( wr_dval ), // 1T after wr_en
        .wr_din_i      ( wr_din  ), // 1T after wr_en
        .full_o        ( full_o  ),
        
        .rd_en_i       ( rd_data_en_i ),
        .rd_dout_o     ( rd_dout_o    ),
        .empty_o       ( data_empty_o ),
        .ram_dp_cfg_register        (mbist_test ? 12'h208  : ram_dp_cfg_register)
    );
`endif

`ifdef FPGA
native_fifo_len #( .DW(LEN_DWIDTH), .AW(LEN_AWIDTH) ) len_fifo(
        .wclk          ( wclk      ),
        .wr_rstn       ( rstn_wclk ),
        .rclk          ( rclk      ),
        .rd_rstn       ( rstn_rclk ),

        .wr_en_i       ( last_d1 ), // 1T after s_axis_tlast_i
        .wr_din_i      ( len_cnt ), // 1T after wr_en
        .full_o        (         ),
        
        .rd_en_i       ( rd_leng_en_i ),
        .rd_dout_o     ( rd_leng_o    ),
        .empty_o       ( empty_o      )
    );

native_fifo_data #( .DW(DMA_DWIDTH), .AW(MEM_AWIDTH) ) data_fifo(
        .wclk          ( wclk      ),
        .wr_rstn       ( rstn_wclk ),
        .rclk          ( rclk      ),
        .rd_rstn       ( rstn_rclk ),

        .wr_en_i       ( wr_dval ), // 1T after wr_en
        .wr_din_i      ( wr_din  ), // 1T after wr_en
        .full_o        ( full_o  ),
        
        .rd_en_i       ( rd_data_en_i ),
        .rd_dout_o     ( rd_dout_o    ),
        .empty_o       ( data_empty_o )
    );
`else
`endif

endmodule
